// This file is part of Keepass2Android, Copyright 2025 Philipp Crocoll.
//
//   Keepass2Android is free software: you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   Keepass2Android is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public License
//   along with Keepass2Android.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Linq;

using Android.App.Assist;
using Android.Content;
using Android.Preferences;
using Android.Views.Autofill;
using DomainNameParser;
using keepass2android;
using Kp2aAutofillParser;
using Newtonsoft.Json;

namespace keepass2android.services.AutofillBase
{
  public class ViewNodeInputField : Kp2aAutofillParser.InputField
  {
    public ViewNodeInputField(AssistStructure.ViewNode viewNode)
    {
      ViewNode = viewNode;
      IdEntry = viewNode.IdEntry;
      Hint = viewNode.Hint;
      ClassName = viewNode.ClassName;
      AutofillHints = viewNode.GetAutofillHints();
      IsFocused = viewNode.IsFocused;
      InputType = (Kp2aAutofillParser.InputTypes)((int)viewNode.InputType);
      HtmlInfoTag = viewNode.HtmlInfo?.Tag;
      HtmlInfoTypeAttribute = viewNode.HtmlInfo?.Attributes?.FirstOrDefault(p => p.First?.ToString() == "type")?.Second?.ToString();
    }

    [JsonIgnore]
    public AssistStructure.ViewNode ViewNode { get; set; }

    public override void FillFilledAutofillValue(FilledAutofillField filledField)
    {
      AutofillValue autofillValue = ViewNode.AutofillValue;
      if (autofillValue != null)
      {
        if (autofillValue.IsList)
        {
          string[] autofillOptions = ViewNode.GetAutofillOptions();
          int index = autofillValue.ListValue;
          if (autofillOptions != null && autofillOptions.Length > 0)
          {
            filledField.TextValue = autofillOptions[index];
          }
        }
        else if (autofillValue.IsDate)
        {
          filledField.DateValue = autofillValue.DateValue;
        }
        else if (autofillValue.IsText)
        {
          filledField.TextValue = autofillValue.TextValue;
        }
      }
    }
  }

  /// <summary>
  /// Converts an AssistStructure into a list of InputFields
  /// </summary>
  class AutofillViewFromAssistStructureFinder
  {
    private readonly Context _context;
    private readonly AssistStructure _structure;
    private PublicSuffixRuleCache domainSuffixParserCache;

    public AutofillViewFromAssistStructureFinder(Context context, AssistStructure structure)
    {
      _context = context;
      _structure = structure;
      domainSuffixParserCache = new PublicSuffixRuleCache(context);
    }

    public AutofillView<ViewNodeInputField> GetAutofillView()
    {
      AutofillView<ViewNodeInputField> autofillView = new AutofillView<ViewNodeInputField>();


      int nodeCount = _structure.WindowNodeCount;
      for (int i = 0; i < nodeCount; i++)
      {
        var node = _structure.GetWindowNodeAt(i);

        var view = node.RootViewNode;
        ParseRecursive(autofillView, view);
      }

      autofillView.PackageId = autofillView.PackageId ?? _structure.ActivityComponent.PackageName;

      return autofillView;

    }


    void ParseRecursive(AutofillView<ViewNodeInputField> autofillView, AssistStructure.ViewNode viewNode)
    {
      String webDomain = viewNode.WebDomain;
      if ((autofillView.PackageId == null) && (!string.IsNullOrWhiteSpace(viewNode.IdPackage)) &&
          (viewNode.IdPackage != "android"))
      {
        autofillView.PackageId = viewNode.IdPackage;
      }

      DomainName outDomain;
      if (DomainName.TryParse(webDomain, domainSuffixParserCache, out outDomain))
      {
        webDomain = outDomain.RawDomainName;
      }

      if (webDomain != null)
      {
        if (!string.IsNullOrEmpty(autofillView.WebDomain))
        {
          if (webDomain != autofillView.WebDomain)
          {
            throw new Java.Lang.SecurityException($"Found multiple web domains: valid= {autofillView.WebDomain}, child={webDomain}");
          }
        }
        else
        {
          autofillView.WebDomain = webDomain;
        }
      }

      autofillView.InputFields.Add(new ViewNodeInputField(viewNode));
      var childrenSize = viewNode.ChildCount;
      if (childrenSize > 0)
      {
        for (int i = 0; i < childrenSize; i++)
        {
          ParseRecursive(autofillView, viewNode.GetChildAt(i));
        }
      }
    }
  }

  /// <summary>
  ///	Parser for an AssistStructure object. This is invoked when the Autofill Service receives an
  /// AssistStructure from the client Activity, representing its View hierarchy. In this sample, it
  /// parses the hierarchy and collects autofill metadata from {@link ViewNode}s along the way.
  /// </summary>
  public sealed class StructureParser : StructureParserBase<ViewNodeInputField>
  {
    private readonly AssistStructure _structure;
    public Context _context { get; }
    public AutofillFieldMetadataCollection AutofillFields { get; set; }
    public FilledAutofillFieldCollection<ViewNodeInputField> ClientFormData { get; set; }

    public string PackageId { get; set; }

    public StructureParser(Context context, AssistStructure structure)
    : base(new Kp2aLogger(), new Kp2aDigitalAssetLinksDataSource(context))
    {
      _context = context;
      _structure = structure;
      AutofillFields = new AutofillFieldMetadataCollection();
      LogAutofillView = PreferenceManager.GetDefaultSharedPreferences(context).GetBoolean(context.GetString(Resource.String.LogAutofillView_key), false);

    }

    protected override AutofillTargetId Parse(bool forFill, AutofillView<ViewNodeInputField> autofillView)
    {
      if (autofillView == null)
        Kp2aLog.Log("Received null autofill view!");
      var result = base.Parse(forFill, autofillView);

      Kp2aLog.Log("Parsing done");

      if (forFill)
      {
        foreach (var p in FieldsMappedToHints)
          AutofillFields.Add(new AutofillFieldMetadata(p.Key.ViewNode, p.Value));
      }
      else
      {
        ClientFormData = new FilledAutofillFieldCollection<ViewNodeInputField>();
        foreach (var p in FieldsMappedToHints)
          ClientFormData.Add(new FilledAutofillField(p.Key, p.Value));
      }


      return result;
    }

    public AutofillTargetId ParseForSave()
    {
      var autofillView = new AutofillViewFromAssistStructureFinder(_context, _structure).GetAutofillView();
      return Parse(false, autofillView);
    }

    public StructureParserBase<ViewNodeInputField>.AutofillTargetId ParseForFill()
    {
      var autofillView = new AutofillViewFromAssistStructureFinder(_context, _structure).GetAutofillView();
      return Parse(true, autofillView);
    }


  }

  public class Kp2aLogger : ILogger
  {
    public void Log(string x)
    {
      Kp2aLog.Log(x);
    }
  }
}
